//*****************************************************************************
// JFab (http://code.google.com/p/jfab)
// Copyright (c) 2011-2012 JFab.org
// Admin jfab@jeffreythompson.net
//
// See the file "LICENSE.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//*****************************************************************************

package org.jfab.swingui;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.KeyStroke;

import org.apache.commons.lang3.StringUtils;

/**
 * Provides a GUI action manager.
 */
public class ActionManager
{
    /** Command accelerator modifier. */
    private static final Integer COMMAND_MODIFIER = new Integer(Toolkit
            .getDefaultToolkit().getMenuShortcutKeyMask());

    /** Shift accelerator modifier. */
    private static final Integer SHIFT_MODIFIER = new Integer(
            ActionEvent.SHIFT_MASK);

    /** Action name to key stroke. */
    private static final Map<String, KeyStroke> ACTION_TO_KEY_STROKE = new HashMap<String, KeyStroke>();

    static
    {
        ACTION_TO_KEY_STROKE.put("new",
                KeyStroke.getKeyStroke(KeyEvent.VK_N, COMMAND_MODIFIER));
        ACTION_TO_KEY_STROKE.put("open",
                KeyStroke.getKeyStroke(KeyEvent.VK_O, COMMAND_MODIFIER));
        ACTION_TO_KEY_STROKE.put("save",
                KeyStroke.getKeyStroke(KeyEvent.VK_S, COMMAND_MODIFIER));
        ACTION_TO_KEY_STROKE.put(
                "saveAs",
                KeyStroke.getKeyStroke(KeyEvent.VK_S, COMMAND_MODIFIER
                        + SHIFT_MODIFIER));
    }

    /** Action name to text. */
    private static final Map<String, String> ACTION_TO_TEXT = new HashMap<String, String>();

    static
    {
        ACTION_TO_TEXT.put("about", "About JFab UI");
        ACTION_TO_TEXT.put("add", "Add");
        ACTION_TO_TEXT.put("generate", "Generate...");
        ACTION_TO_TEXT.put("new", "New");
        ACTION_TO_TEXT.put("open", "Open...");
        ACTION_TO_TEXT.put("remove", "Remove");
        ACTION_TO_TEXT.put("save", "Save");
        ACTION_TO_TEXT.put("saveAs", "Save As...");
        ACTION_TO_TEXT.put("saveCgObject", "Save");
    }

    /** Action name to icon tool tip. */
    private static final Map<String, String> ACTION_TO_TOOL_TIP = new HashMap<String, String>();

    static
    {
        ACTION_TO_TOOL_TIP.put("about",
                "View information about this application.");
        ACTION_TO_TOOL_TIP.put("add", "Add a child to the selected tree node.");
        ACTION_TO_TOOL_TIP.put("collapseAll", "Collapse all nodes.");
        ACTION_TO_TOOL_TIP.put("expandAll", "Expand all nodes.");
        ACTION_TO_TOOL_TIP.put("generate", "Generate Java code files.");
        ACTION_TO_TOOL_TIP.put("new", "Create a new JFab document.");
        ACTION_TO_TOOL_TIP.put("open", "Open a JFab file.");
        ACTION_TO_TOOL_TIP.put("remove", "Remove the selected tree node.");
        ACTION_TO_TOOL_TIP.put("save", "Save a JFab file.");
        ACTION_TO_TOOL_TIP.put("saveAs", "Save as a JFab file.");
        ACTION_TO_TOOL_TIP.put("saveCgObject", "Save item changes.");
    }

    /** Action performer. */
    final ActionPerformer _actionPerformer;

    /** About action. */
    private Action _aboutAction;

    /** Add node action. */
    private Action _addNodeAction;

    /** Collapse all action. */
    private Action _collapseAllAction;

    /** Expand all action. */
    private Action _expandAllAction;

    /** Generate action. */
    private Action _generateAction;

    /** Icon manager. */
    private final IconManager _iconManager;

    /** New action. */
    private Action _newAction;

    /** Open action. */
    private Action _openAction;

    /** Remove node action. */
    private Action _removeNodeAction;

    /** Save action. */
    private Action _saveAction;

    /** Save As action. */
    private Action _saveAsAction;

    /** Save code generator object action. */
    private Action _saveCgObjectAction;

    /**
     * Construct this object with the given parameters.
     * 
     * @param subjectObject Subject object.
     * @param iconManager Icon manager.
     */
    public ActionManager(final JFabUI subjectObject,
            final IconManager iconManager)
    {
        _actionPerformer = subjectObject;
        _iconManager = iconManager;
    }

    /**
     * @return the aboutAction
     */
    public Action getAboutAction()
    {
        if (_aboutAction == null)
        {
            _aboutAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.aboutActionPerformed(event);
                }
            };

            configureAction(_aboutAction, "about");
        }

        return _aboutAction;
    }

    /**
     * @return the addNodeAction
     */
    public Action getAddNodeAction()
    {
        if (_addNodeAction == null)
        {
            _addNodeAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.addNodeActionPerformed(event);
                }
            };

            configureAction(_addNodeAction, "addNode");
        }

        return _addNodeAction;
    }

    /**
     * @return the collapseAllAction
     */
    public Action getCollapseAllAction()
    {
        if (_collapseAllAction == null)
        {
            _collapseAllAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.collapseAllActionPerformed(event);
                }
            };

            configureAction(_collapseAllAction, "collapseAll");
        }

        return _collapseAllAction;
    }

    /**
     * @return the expandAllAction
     */
    public Action getExpandAllAction()
    {
        if (_expandAllAction == null)
        {
            _expandAllAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.expandAllActionPerformed(event);
                }
            };

            configureAction(_expandAllAction, "expandAll");
        }

        return _expandAllAction;
    }

    /**
     * @return the generateAction
     */
    public Action getGenerateAction()
    {
        if (_generateAction == null)
        {
            _generateAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.generateActionPerformed(event);
                }
            };

            configureAction(_generateAction, "generate");
        }

        return _generateAction;
    }

    /**
     * @return the iconManager
     */
    public IconManager getIconManager()
    {
        return _iconManager;
    }

    /**
     * @return the newAction
     */
    public Action getNewAction()
    {
        if (_newAction == null)
        {
            _newAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.newActionPerformed(event);
                }
            };

            configureAction(_newAction, "new");
        }

        return _newAction;
    }

    /**
     * @return the openAction
     */
    public Action getOpenAction()
    {
        if (_openAction == null)
        {
            _openAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.openActionPerformed(event);
                }
            };

            configureAction(_openAction, "open");
        }

        return _openAction;
    }

    /**
     * @return the removeNodeAction
     */
    public Action getRemoveNodeAction()
    {
        if (_removeNodeAction == null)
        {
            _removeNodeAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.removeNodeActionPerformed(event);
                }
            };

            configureAction(_removeNodeAction, "removeNode");
        }

        return _removeNodeAction;
    }

    /**
     * @return the saveAction
     */
    public Action getSaveAction()
    {
        if (_saveAction == null)
        {
            _saveAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.saveActionPerformed(event);
                }
            };

            configureAction(_saveAction, "save");
        }

        return _saveAction;
    }

    /**
     * @return the saveAsAction
     */
    public Action getSaveAsAction()
    {
        if (_saveAsAction == null)
        {
            _saveAsAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.saveAsActionPerformed(event);
                }
            };

            configureAction(_saveAsAction, "saveAs");
        }

        return _saveAsAction;
    }

    /**
     * @return the saveAction
     */
    public Action getSaveCgObjectAction()
    {
        if (_saveCgObjectAction == null)
        {
            _saveCgObjectAction = new AbstractAction()
            {
                /** Serial version UID. */
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(final ActionEvent event)
                {
                    _actionPerformer.saveCgObjectActionPerformed(event);
                }
            };

            configureAction(_saveCgObjectAction, "saveCgObject");
        }

        return _saveCgObjectAction;
    }

    /**
     * @param action Action.
     * @param actionName Action name.
     */
    private void configureAction(final Action action, final String actionName)
    {
        final Icon icon = _iconManager.getIcon(actionName);

        if (icon != null)
        {
            action.putValue(Action.SMALL_ICON, icon);
        }

        final String text = ACTION_TO_TEXT.get(actionName);

        if (StringUtils.isNotEmpty(text))
        {
            action.putValue(Action.NAME, text);
        }

        final String toolTipText = ACTION_TO_TOOL_TIP.get(actionName);

        if (StringUtils.isNotEmpty(toolTipText))
        {
            action.putValue(Action.SHORT_DESCRIPTION, toolTipText);
        }

        final KeyStroke accelerator = ACTION_TO_KEY_STROKE.get(actionName);

        if (accelerator != null)
        {
            action.putValue(Action.ACCELERATOR_KEY, accelerator);
        }
    }
}
